package client

import (
	"net"
	"sync"

	"github.com/golang/protobuf/proto"
	"pac/packet"
)

type Type int32

const (
	Device Type = 1
	User   Type = 2
)

type Client interface {
	GetConn() net.Conn
	GetType() Type
	GetID() string
	Close()
	Send([]byte) error
	SendPacket(packet packet.Packet) error
	SendReqPb(pb proto.Message) (err error)
}

var cliMap = make(map[string]map[net.Conn]Client)
var cliMapMu sync.Mutex

func PutClient(cli Client) {
	if cli == nil {
		return
	}
	username := cli.GetID()
	if len(username) == 0 {
		return
	}
	cliMapMu.Lock()
	defer cliMapMu.Unlock()
	subCliMap := cliMap[username]
	if subCliMap == nil {
		subCliMap = make(map[net.Conn]Client)
		subCliMap[cli.GetConn()] = cli
		cliMap[username] = subCliMap
	} else {
		if subCliMap[cli.GetConn()] == nil {
			subCliMap[cli.GetConn()] = cli
		}
	}
}

func FindClient(id string) []Client {
	if len(id) == 0 {
		return nil
	}
	var cliSlice []Client
	cliMapMu.Lock()
	defer cliMapMu.Unlock()
	subCliMap := cliMap[id]
	if subCliMap != nil {
		for _, cli := range subCliMap {
			cliSlice = append(cliSlice, cli)
		}
	}
	return cliSlice
}

func RemoveClient(cli Client) (removed bool, stillOnline bool) {
	if cli == nil {
		return false, false
	}
	id := cli.GetID()
	if len(id) == 0 {
		return false, false
	}
	conn := cli.GetConn()

	cliMapMu.Lock()
	defer cliMapMu.Unlock()

	if conn == nil {
		return false, len(cliMap[id]) != 0
	}

	subCliMap := cliMap[id]
	if subCliMap == nil {
		return false, false
	}

	_, exists := subCliMap[conn]
	if exists {
		delete(subCliMap, conn)
		if len(subCliMap) == 0 {
			delete(cliMap, id)
			return true, false
		}
		return true, true
	}

	return false, len(subCliMap) != 0
}

func GetAllClient(t Type) []Client {
	var cliSlice []Client
	cliMapMu.Lock()
	defer cliMapMu.Unlock()
	for _, subCliMap := range cliMap {
		if subCliMap != nil {
			for _, cli := range subCliMap {
				if cli.GetType() == t {
					cliSlice = append(cliSlice, cli)
				}
			}
		}
	}
	return cliSlice
}

func NotifToUser(msg proto.Message) {
	for _, cli := range GetAllClient(User) {
		cli.SendReqPb(msg)
	}
}
